/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[欢迎加盟知启蒙，一起邂逅框架梦]
 * 
 * https://www.zhiqim.com/gitcan/zhiqim/zhiqim_account.htm
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zhiqim.account.presenter;

import java.util.List;

import org.zhiqim.account.ZacConstants;
import org.zhiqim.account.ZacPassworder;
import org.zhiqim.account.ZacSessionUser;
import org.zhiqim.account.api.connect.IUserInfo;
import org.zhiqim.account.dao.UserPassportDao;
import org.zhiqim.account.dbo.user.UserAccount;
import org.zhiqim.account.dbo.user.UserInfo;
import org.zhiqim.account.dbo.user.UserVerification;
import org.zhiqim.account.paycallback.MergeCallback;
import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.httpd.context.ZmlContextConstants;
import org.zhiqim.httpd.validate.ones.IsIntegerValue;
import org.zhiqim.httpd.validate.ones.IsLen;
import org.zhiqim.httpd.validate.ones.IsNotEmpty;
import org.zhiqim.httpd.validate.ones.IsNumericLen;
import org.zhiqim.httpd.validate.onex.IsRegexp;
import org.zhiqim.kernel.Global;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.annotation.AnTransaction;
import org.zhiqim.kernel.constants.RegexpConstants;
import org.zhiqim.kernel.util.DateTimes;
import org.zhiqim.kernel.util.Ids;
import org.zhiqim.kernel.util.Randoms;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.kernel.util.consts.Int;
import org.zhiqim.mail.MailSender;
import org.zhiqim.orm.ZTable;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;
import org.zhiqim.sms.SMSSender;

/**
 * 用户通行证处理器
 *
 * @version v1.0.0 @author zhichenggang 2017-7-2 新建与整理
 */
@AnAlias("UserPassportPresenter")
public class UserPassportPresenter implements ZmlContextConstants, ZacConstants
{
    /** 登录 */
    public static void login(HttpRequest request) throws Exception
    {
        // 自动登录标记符
        String isAuto = request.getParameter("isAuto");

        //1.检查参数
        request.addValidate(new IsNotEmpty("userAccount", "用户账号不能为空"));
        if (Validates.isEmpty(isAuto))
            request.addValidate(new IsRegexp("userPass", "密码要求6-16位的大小写字母和.@_#$符号", RegexpConstants.USER_PASS));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查账号
        String userAccount = request.getParameter("userAccount");
        String userPass = request.getParameter("userPass");
        String isRemember = request.getParameter("isRemember");
        
        UserAccount account = Global.get(ZTable.class).item(UserAccount.class, userAccount);
        if (account == null)
        {//账号不存在
            request.setResponseError("用户账号或密码不正确");
            return;
        }
        
        UserInfo user = Global.get(ZTable.class).item(UserInfo.class, account.getUserId());
        if (user == null)
        {//账号异常
            request.setResponseError("用户账号异常，请与客服联系");
            return;
        }
        
        if (Validates.isEmptyBlank(user.getUserPass()))
        {//账号异常
            request.setResponseError("用户账号未设置密码，请使用QQ或微信登录后设置密码才能使用用户名登录");
            return;
        }
        
        //3.比较密码
        String userPassMd5 = ZacPassworder.encode(user.getUserId(), userPass, user.getUserPassSalt());
        if (Validates.isEmpty(isAuto))
        {
            if(!user.getUserPass().equalsIgnoreCase(userPassMd5))
            {
                request.setResponseError("用户账号或密码不正确");
                return;
            }
        }
        else
        {
            if(!user.getUserPass().equalsIgnoreCase(userPass))
            {
                request.removeCookieToResponse("userAccount");
                request.removeCookieToResponse("userPass");
                request.removeCookieToResponse("isRemember");
                return;
            }
        }
        
        //4.绑定会话用户
        List<UserAccount> accountList = Global.get(ZTable.class).list(UserAccount.class, new Selector("userId", account.getUserId()));
        ZacSessionUser sessionUser = new ZacSessionUser();
        sessionUser.setUserAccount(userAccount);//登录账号
        sessionUser.setUser(user);
        sessionUser.setAccountList(accountList);
        
        request.bindSessionUser(sessionUser);
        
        //5.对记住账号的写入或删除浏览器中
        if(Validates.isNotEmpty(isRemember))
        {
            request.addCookieToResponse("userAccount", userAccount, 60*60*24*7);
            request.addCookieToResponse("userPass", user.getUserPass(), 60*60*24*7);
            request.addCookieToResponse("isRemember", isRemember, 60*60*24*7);
        }
        else
        {
            request.removeCookieToResponse("userAccount");
            request.removeCookieToResponse("userPass");
            request.removeCookieToResponse("isRemember");
        }
    }
    
    /** 第三方绑定 */
    @AnTransaction
    public static void bindByConnect(HttpRequest request, IUserInfo userInfo) throws Exception
    {
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        UserAccount account = Global.get(ZTable.class).item(UserAccount.class, userInfo.getUserAccount());
        int accountType = userInfo.getAccountType();
        String accountName = "";
        if (accountType == ACCOUNT_TYPE_QQ)
            accountName = "QQ";
        else if(accountType == ACCOUNT_TYPE_WXOP)
            accountName = "微信";
        
        if (account == null)
        {
            account = new UserAccount();
            account.setUserId(sessionUser.getUserId());
            account.setUserAccount(userInfo.getUserAccount());
            account.setAccountType(userInfo.getAccountType());
            account.setAccountUnion(userInfo.getAccountUnion());
            Global.get(ZTable.class).insert(account);
            
            request.setRedirectParent("/account/security.htm", "绑定成功");
            return;
        }
        else
        {
            UserInfo user = Global.get(ZTable.class).item(UserInfo.class, account.getUserId());
            if (user == null)
            {
                request.setRedirectParent("/account/security.htm", "该"+accountName+"号已绑定其他账户");
                return;
            }
            else
            {
                if (Validates.isEmptyBlank(user.getUserName()))
                {
                    request.setRedirectParent("/account/security.htm", "该"+accountName+"号已绑定其他账户");
                    return;
                }
                else
                {
                    request.setRedirectParent("/account/security.htm", "该"+accountName+"号已绑定其他账户[用户名："+user.getUserName()+"]");
                    return;
                }
            }
        }
    }
    
    /** 第三方登录 */
    @AnTransaction
    public static void loginByConnect(HttpRequest request, IUserInfo userInfo) throws Exception
    {
        //1.检查是否已自动注册
        UserAccount account = Global.get(ZTable.class).item(UserAccount.class, userInfo.getUserAccount());
        if (account == null)
        {
            if (Validates.isNotEmpty(userInfo.getAccountUnion()))
            {//如果有联合ID，查联合ID对应的账号是否已自动注册，已注册则绑定即可
                UserAccount union = Global.get(ZTable.class).item(UserAccount.class, new Selector("accountUnion", userInfo.getAccountUnion()));
                if (union != null)
                {
                    account = new UserAccount();
                    account.setUserId(union.getUserId());
                    account.setUserAccount(userInfo.getUserAccount());
                    account.setAccountType(userInfo.getAccountType());
                    account.setAccountUnion(userInfo.getAccountUnion());
                    
                    Global.get(ZTable.class).insert(account);
                }
            }
        }
        
        UserInfo user = null;
        if (account == null)
        {//自动注册
            long userId = Ids.longId13();
            
            account = new UserAccount();
            account.setUserId(userId);
            account.setUserAccount(userInfo.getUserAccount());
            account.setAccountType(userInfo.getAccountType());
            account.setAccountUnion(userInfo.getAccountUnion());
            
            user = new UserInfo();
            user.setUserId(userId);                             //用户编号
            user.setUserNick(userInfo.getUserNick());           //用户昵称，默认手机号或邮箱
            user.setUserRegistered(userInfo.getAccountType());  //用户来源
            user.setUserStatus(1);                              //用户状态，1表示正常
            user.setUserBalance(0);                             //用户余额，默认0元
            user.setUserSex(userInfo.getUserSex());             //用户性别
            user.setUserAvatar50(userInfo.getUserAvatar50());   //用户头像
            user.setUserAvatar100(userInfo.getUserAvatar100()); //用户头像
            user.setUserAvatar150(userInfo.getUserAvatar150()); //用户头像
            user.setUserCreated(Sqls.nowTimestamp());           //用户注册时间
            user.setUserModified(user.getUserCreated());        //用户最后修改时间
            user.setUserLogined(user.getUserCreated());         //用户最后登录时间
            
            Global.get(ZTable.class).insert(account);
            Global.get(ZTable.class).insert(user);
        }
        else
        {//修改登录信息
            user = Global.get(ZTable.class).item(UserInfo.class, account.getUserId());
            if (Validates.isEmpty(user.getUserName()))
            {//没有用户名，重新刷新昵称,头像,性别,登录时间
                Global.get(ZTable.class).update(UserAccount.class, 
                    new Updater()
                    .addMust("userAccount", userInfo.getUserAccount())
                    .addField("accountUnion", userInfo.getAccountUnion()));

                Updater updater = new Updater();
                updater.addMust("userId", account.getUserId());
                updater.addField("userNick", userInfo.getUserNick());
                updater.addField("userSex", userInfo.getUserSex());
                updater.addField("userAvatar50", userInfo.getUserAvatar50());
                updater.addField("userAvatar100", userInfo.getUserAvatar100());
                updater.addField("userAvatar150", userInfo.getUserAvatar150());
                updater.addField("userLogined", Sqls.nowTimestamp());
                Global.get(ZTable.class).update(UserInfo.class, updater);
            }
            else
            {//只刷新性别,登录时间
                Global.get(ZTable.class).update(UserAccount.class, 
                    new Updater()
                    .addMust("userAccount", userInfo.getUserAccount())
                    .addField("accountUnion", userInfo.getAccountUnion()));

                Updater updater = new Updater();
                updater.addMust("userId", account.getUserId());
                updater.addField("userSex", userInfo.getUserSex());
                updater.addField("userLogined", Sqls.nowTimestamp());
                Global.get(ZTable.class).update(UserInfo.class, updater);
            }
        }
        
        if (Validates.isEmpty(user.getUserName()) && account.getAccountType() != ACCOUNT_TYPE_WXMP)
        {//判断是否有用户名，没有就跳到注册第二步
            request.setSessionAttribute("userId", user.getUserId());
            request.setSessionAttribute("userNick", user.getUserNick());
            request.setRedirect("/registerSubmit.htm");
            return;
        }
        
        List<UserAccount> accountList = Global.get(ZTable.class).list(UserAccount.class, new Selector("userId", account.getUserId()));
        ZacSessionUser sessionUser = new ZacSessionUser();
        sessionUser.setUser(user);
        sessionUser.setUserAccount(userInfo.getUserAccount());
        sessionUser.setAccountList(accountList);
        
        //绑定用户
        request.bindSessionUser(sessionUser);
    }
    
    /** 注册第一步（填手机/邮箱和验证码） */
    public static void registerUserAccount(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsNotEmpty("userAccount", "请输入邮箱或手机号"));
        request.addValidate(new IsNumericLen("validCode", "请输入正确的验证码", 6, 6));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查验证码
        String userAccount = request.getParameter("userAccount");
        String validCode = request.getParameter("validCode");
        
        int accountType = chkUserAccount(userAccount);
        if (accountType == 0)
        {
            request.setResponseError("请输入正确的邮箱或手机号");
            return;
        }
        
        String result = chkValidCode(userAccount, validCode, 0);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
        
        //3.检查用户是否已注册
        result = chkRegister(userAccount, accountType);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
    }
    
    /** 注册提交 */
    @AnTransaction
    public static void register(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsNotEmpty("userName", "请输入用户名"));
        request.addValidate(new IsLen("userNick", "请输入用户昵称", 1, 32));
        request.addValidate(new IsRegexp("userPass", "密码要求6-16位的大小写字母和.@_#$符号", RegexpConstants.USER_PASS));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查用户名是否合法
        long userId = request.getParameterLong("userId");
        String userAccount = request.getParameter("userAccount");//用户账号
        int accountType = chkUserAccount(userAccount);
        
        String userName = request.getParameter("userName");
        String userNick = request.getParameter("userNick");
        String userPass = request.getParameter("userPass");
        if (!Validates.isAlphaNumericUlLen(userName, 1, 20))
        {
            request.setResponseError("用户名必须为字母,数字或(.-_~)组合，[1, 20]范围");
            return;
        }
        
        if (!Validates.isFileName(userName))
        {
            request.setResponseError("用户名不合法，请重选用户名");
            return;
        }
        
        //3.检查用户名和昵称唯一性
        if(Global.get(ZTable.class).count(UserAccount.class, userName) > 0 )
        {
            request.setResponseError("用户名已被使用，请重新输入");
            return;
        }
        
        if(Global.get(ZTable.class).count(UserInfo.class, new Selector("userName", userName)) > 0 )
        {
            request.setResponseError("用户名已被使用，请重新输入");
            return;
        }
        
        //4.保存到用户账号&用户信息
        if (userId == -1)
        {//手动注册
            if (Validates.isEmpty(userAccount))
            {
                request.setResponseError("请按注册步骤操作");
                return;
            }
            
            if(Global.get(ZTable.class).count(UserInfo.class, new Selector("userNick", userNick)) > 0 )
            {
                request.setResponseError("昵称已被使用，请重新输入");
                return;
            }
            
            userId = Ids.longId13();
            String userPassSalt = Randoms.lettersDigits(64);
            String md5Pass = ZacPassworder.encode(userId, userPass, userPassSalt);
            
            //用户账号---手机/邮箱
            UserAccount account = new UserAccount();
            account.setUserId(userId);
            account.setUserAccount(userAccount);
            account.setAccountType(accountType);
            
            //用户账号---用户名
            UserAccount account1 = new UserAccount();
            account1.setUserId(userId);
            account1.setUserAccount(userName);
            account1.setAccountType(ACCOUNT_TYPE_USERNAME);
            
            UserInfo user = new UserInfo();
            user.setUserId(userId);                             //用户编号
            user.setUserNick(userNick);                         //用户昵称，默认手机号或邮箱
            user.setUserRegistered(accountType);                //用户来源，100本站手机，110本站邮箱
            user.setUserStatus(1);                              //用户状态，1表示正常
            user.setUserBalance(0);                             //用户余额，默认0元
            
            if (accountType == ACCOUNT_TYPE_MOBILE)
                user.setUserMobile(userAccount);                   //用户绑定手机
            else
                user.setUserEmail(userAccount);                    //用户绑定邮箱
            user.setUserPass(md5Pass);                          //用户密码
            user.setUserPassSalt(userPassSalt);                 //用户密码盐值
            
            user.setUserCreated(Sqls.nowTimestamp());           //用户注册时间
            user.setUserModified(user.getUserCreated());        //用户最后修改时间
            user.setUserLogined(user.getUserCreated());         //用户最后登录时间
            user.setUserName(userName);                             //用户名
            
            //插入账号和信息
            Global.get(ZTable.class).insert(account);
            Global.get(ZTable.class).insert(account1);
            Global.get(ZTable.class).insert(user);
        }
        else
        {//没有用户名（第三方登录）
            if(Global.get(ZTable.class).count(UserInfo.class, new Selector("userNick", userNick).addMustNotEqual("userId", userId)) > 0 )
            {
                request.setResponseError("昵称已被使用，请重新输入");
                return;
            }
            
            //用户账号---用户名
            UserAccount account = new UserAccount();
            account.setUserId(userId);
            account.setUserAccount(userName);
            account.setAccountType(ACCOUNT_TYPE_USERNAME);
            Global.get(ZTable.class).insert(account);
            
            String userPassSalt = Randoms.lettersDigits(64);
            String md5Pass = ZacPassworder.encode(userId, userPass, userPassSalt);
            
            Updater updater = new Updater();
            updater.addMust("userId", userId);
            updater.addField("userNick", userNick);
            updater.addField("userPass", md5Pass);
            updater.addField("userPassSalt", userPassSalt);
            updater.addField("userModified", Sqls.nowTimestamp());
            updater.addField("userLogined", Sqls.nowTimestamp());
            updater.addField("userName", userName);//用户名
            Global.get(ZTable.class).update(UserInfo.class, updater);
        }
    }
    
    /** 注销账号 */
    @AnTransaction
    public static void cancelAccount(HttpRequest request) throws Exception
    {
        long userId = request.getParameterLong("userId");
        if (userId == -1)
        {
            request.setResponseError("注销失败");
            return;
        }
        
        Global.get(ZTable.class).delete(UserInfo.class, userId);
        Global.get(ZTable.class).delete(UserAccount.class, new Selector("userId", userId));
    }
    
    /** 合并账号 */
    @AnTransaction
    public static void mergeAccount(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsNotEmpty("userAccount", "请输入邮箱或手机号"));
        request.addValidate(new IsNumericLen("validCode", "请输入正确的验证码", 6, 6));
        request.addValidate(new IsRegexp("userPass", "密码要求6-16位的大小写字母和.@_#$符号", RegexpConstants.USER_PASS));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查验证码
        String userAccount = request.getParameter("userAccount");
        String validCode = request.getParameter("validCode");
        String userPass = request.getParameter("userPass");
        
        int accountType = chkUserAccount(userAccount);
        if (accountType == 0)
        {
            request.setResponseError("请输入正确的邮箱或手机号");
            return;
        }
        
        String result = chkValidCode(userAccount, validCode, 4);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
        
        //3.检查用户账号和密码
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        UserAccount account = Global.get(ZTable.class).item(UserAccount.class, userAccount);
        
        UserInfo user = Global.get(ZTable.class).item(UserInfo.class, account.getUserId());
        String userPassMd5 = ZacPassworder.encode(user.getUserId(), userPass, user.getUserPassSalt());
        if(!user.getUserPass().equalsIgnoreCase(userPassMd5))
        {
            request.setResponseError("用户账号或密码不正确");
            return;
        }
        
        if (account.getUserId() == sessionUser.getUserId())
        {
            request.setResponseError("子账号和本账号一致，不需要合并");
            return;
        }
        
        //4.比较信息，更新信息
        UserInfo localUser = Global.get(ZTable.class).item(UserInfo.class, sessionUser.getUserId());
        
        Updater updater = new Updater();
        updater.addMust("userId", sessionUser.getUserId());
        updater.addFieldExpression("userBalance", "userBalance+"+user.getUserBalance());//余额相加
        
        if (Validates.isEmpty(localUser.getUserMobile()) && Validates.isNotEmpty(user.getUserMobile()))
        {//本账号没绑定手机号，子账号绑定手机号
            updater.addField("userMobile", user.getUserMobile());
            
            if (Global.get(ZTable.class).count(UserAccount.class, user.getUserMobile()) > 0)
            {
                Global.get(ZTable.class).delete(UserAccount.class, user.getUserMobile());
                
                UserAccount useraccount = new UserAccount();
                useraccount.setUserId(sessionUser.getUserId());
                useraccount.setUserAccount(user.getUserMobile());
                useraccount.setAccountType(ACCOUNT_TYPE_MOBILE);
                Global.get(ZTable.class).insert(useraccount);
            }
        }
        else if(Validates.isNotEmpty(localUser.getUserMobile()) && Validates.isNotEmpty(user.getUserMobile()))
        {//本账号绑定手机号，子账号绑定手机号
            if (Global.get(ZTable.class).count(UserAccount.class, new Selector("userId", account.getUserId()).addMust("userAccount", user.getUserMobile())) > 0)
                Global.get(ZTable.class).delete(UserAccount.class, user.getUserMobile());
        }
        
        if (Validates.isEmpty(localUser.getUserEmail()) && Validates.isNotEmpty(user.getUserEmail()))
        {//本账号没绑定邮箱号，子账号绑定邮箱号
            updater.addField("userEmail", user.getUserEmail());
            
            if (Global.get(ZTable.class).count(UserAccount.class, user.getUserEmail()) > 0)
            {
                Global.get(ZTable.class).delete(UserAccount.class, user.getUserEmail());
                
                UserAccount useraccount = new UserAccount();
                useraccount.setUserId(sessionUser.getUserId());
                useraccount.setUserAccount(user.getUserEmail());
                useraccount.setAccountType(ACCOUNT_TYPE_EMAIL);
                Global.get(ZTable.class).insert(useraccount);
            }
        }
        else if(Validates.isNotEmpty(localUser.getUserEmail()) && Validates.isNotEmpty(user.getUserEmail()))
        {//本账号绑定邮箱号，子账号绑定邮箱号
            if (Global.get(ZTable.class).count(UserAccount.class, new Selector("userId", account.getUserId()).addMust("userAccount", user.getUserEmail())) > 0)
                Global.get(ZTable.class).delete(UserAccount.class, user.getUserEmail());
        }
        
        if (!UserPassportDao.bindByQQ(sessionUser.getUserId()) && UserPassportDao.bindByQQ(account.getUserId()))
        {//本账号没绑定QQ号，子账号绑定QQ号
            UserAccount useraccountQQ = Global.get(ZTable.class).item(UserAccount.class, new Selector("userId", account.getUserId()).addMust("accountType", ACCOUNT_TYPE_QQ));
            String userAccountQQ = useraccountQQ.getUserAccount();
            String accountUnionQQ = useraccountQQ.getAccountUnion();
            
            Global.get(ZTable.class).delete(UserAccount.class, userAccountQQ);
            
            UserAccount accountQQ = new UserAccount();
            accountQQ.setUserId(sessionUser.getUserId());
            accountQQ.setUserAccount(userAccountQQ);
            accountQQ.setAccountType(ACCOUNT_TYPE_QQ);
            accountQQ.setAccountUnion(accountUnionQQ);
            Global.get(ZTable.class).insert(accountQQ);
        }
        else if (UserPassportDao.bindByQQ(sessionUser.getUserId()) && UserPassportDao.bindByQQ(account.getUserId()))
        {//本账号绑定QQ号，子账号绑定QQ号
            Global.get(ZTable.class).delete(UserAccount.class, new Selector("userId", account.getUserId()).addMust("accountType", ACCOUNT_TYPE_QQ));
        }
    
        if (!UserPassportDao.bindByWx(sessionUser.getUserId()) && UserPassportDao.bindByWx(account.getUserId()))
        {//本账号没绑定微信号，子账号绑定微信号
            UserAccount useraccountWx = Global.get(ZTable.class).item(UserAccount.class, new Selector("userId", account.getUserId()).addMust("accountType", ACCOUNT_TYPE_QQ));
            String userAccountWx = useraccountWx.getUserAccount();
            String accountUnionWx = useraccountWx.getAccountUnion();
            
            Global.get(ZTable.class).delete(UserAccount.class, userAccountWx);
            
            UserAccount accountWx = new UserAccount();
            accountWx.setUserId(sessionUser.getUserId());
            accountWx.setUserAccount(userAccountWx);
            accountWx.setAccountType(ACCOUNT_TYPE_WXOP);
            accountWx.setAccountUnion(accountUnionWx);
            Global.get(ZTable.class).insert(accountWx);
        }
        else if (UserPassportDao.bindByWx(sessionUser.getUserId()) && UserPassportDao.bindByWx(account.getUserId()))
        {//本账号绑定微信号，子账号绑定微信号
            Global.get(ZTable.class).delete(UserAccount.class, new Selector("userId", account.getUserId()).addMust("accountType", ACCOUNT_TYPE_WXOP));
        }
        
        updater.addField("userModified", Sqls.nowTimestamp());
        Global.get(ZTable.class).update(UserInfo.class, updater);
        Global.get(ZTable.class).delete(UserInfo.class, account.getUserId());
        if (Validates.isNotEmpty(user.getUserName()))
            Global.get(ZTable.class).delete(UserAccount.class, user.getUserName());
        
        //组件调用
        if (Global.getClass("MergeAccount") != null)
        {
            Object obj = Global.getCreate(Global.getClass("MergeAccount"));
            MergeCallback call = (MergeCallback)obj;
            String message = call.merge(localUser, user);
            if (!"success".equals(message))
            {
                request.setResponseError(message);
                return;
            }
        }
        
        //5.绑定会话用户
        List<UserAccount> accountList = Global.get(ZTable.class).list(UserAccount.class, new Selector("userId", sessionUser.getUserId()));
        sessionUser.setUserAccount(userAccount);//登录账号
        sessionUser.setUser(localUser);
        sessionUser.setAccountList(accountList);
        
        request.bindSessionUser(sessionUser);
    }
    
    /** 忘记密码 */
    public static void forgetPass(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsNotEmpty("userAccount", "请输入邮箱或手机号"));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查用户是否未注册
        String userAccount = request.getParameter("userAccount");
        int accountType = chkUserAccount(userAccount);
        if (accountType == 0)
        {
            request.setResponseError("请输入正确的邮箱或手机号");
            return;
        }
        
        String result = chkForgetPass(userAccount, accountType);
        if (Validates.isNotEmpty(result))
        {
            request.setResponseError(result);
            return;
        }
    }
    
    /** 忘记密码，提交新密码 */
    public static void forgetPassSubmit(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsNotEmpty("userAccount", "请输入邮箱或手机号"));
        request.addValidate(new IsNumericLen("validCode", "请输入正确的验证码", 6, 6));
        request.addValidate(new IsRegexp("userPass", "密码要求6-16位的大小写字母和.@_#$符号", RegexpConstants.USER_PASS));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查验证码
        String validCode = request.getParameter("validCode");
        String userAccount = request.getParameter("userAccount");
        String userAccountReal = request.getParameter("userAccountReal");
        String userPass = request.getParameter("userPass");
        
        int accountType = chkUserAccount(userAccount);
        if (accountType == 0)
        {
            request.setResponseError("请输入正确的邮箱或手机号");
            return;
        }
        
        String result = chkValidCode(userAccount, validCode, 1);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
        
        //3.检查用户是否已注册
        Object obj = chkUser(userAccountReal);
        if (obj instanceof String)
        {
            request.setResponseError(obj.toString());
            return;
        }
        
        //4.更新用户密码
        long userId = ((UserInfo)obj).getUserId();
        
        String userPassSalt = Randoms.lettersDigits(64);
        String md5Pass = ZacPassworder.encode(userId, userPass, userPassSalt);
        
        Updater updater = new Updater()
            .addMust("userId", userId)
            .addField("userPass", md5Pass)
            .addField("userPassSalt", userPassSalt)
            .addField("userModified", Sqls.nowTimestamp());
        
        Global.get(ZTable.class).update(UserInfo.class, updater);
    }
    
    /** 发送验证码 */
    public static void doSendVerificationCode(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsNotEmpty("userAccount", "请输入邮箱或手机号"));
        request.addValidate(new IsIntegerValue("verificationType", "请选择注册功能或找回密码功能", 0, 1));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查账号
        String userAccount = request.getParameter("userAccount");
        int verificationType = request.getParameterInt("verificationType");
        
        int accountType = chkUserAccount(userAccount);
        if (accountType == 0)
        {
            request.setResponseError("请输入正确的邮箱或手机号");
            return;
        }
        
        String result = verificationType==0?chkRegister(userAccount, accountType):chkForgetPass(userAccount, accountType);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
        
        //3.发送验证码
        String verificationCode = Randoms.digits(6);
        if (Validates.isEmail(userAccount))
        {//发邮箱
            String subject = "知启蒙邮箱注册验证码";
            String content = "【知启蒙】您的注册验证码是：%s，验证码十分钟内有效。";
            content = Strings.format(content, verificationCode);
            
            String ret = Global.getService(MailSender.class).sendTextMail(userAccount, subject, content);
            if(Validates.isNotEmpty(ret))
            {
                request.setResponseError("找回密码邮件发送失败，请重新填写");
                return;
            }
        }
        else
        {//发短信
            Int ret = Global.getService(SMSSender.class).sendVerificationCode(userAccount, verificationCode);
            if(ret.value() != 0)
            {
                request.setResponseError(ret.desc());
                return;
            }
        }
        
        //4.保存验证码到数据库
        UserVerification verification = new UserVerification();
        verification.setVerificationId(Ids.longId19());
        verification.setUserAccount(userAccount);
        verification.setVerificationCode(verificationCode);
        verification.setVerificationStatus(0);
        verification.setVerificationType(verificationType);
        verification.setVerificationDate(DateTimes.getDateString());
        verification.setVerificationEndTime(DateTimes.getNextDateTimeStringBySecond(60*20));//20分钟有效
        Global.get(ZTable.class).insert(verification);
        
        request.setResponseResult("验证码已发送至" + userAccount + "，请验证");
    }
    
    /************************************************************************************/
    //私有方法
    /************************************************************************************/
    
    /** 获取用户账号类型，100：手机号，110：邮箱，0：非手机邮箱 */
    private static int chkUserAccount(String userAccount)
    {
        if (Validates.isMobile11(userAccount))
            return ACCOUNT_TYPE_MOBILE;
        else if (Validates.isEmail(userAccount))
            return ACCOUNT_TYPE_EMAIL;
        else
            return 0;
    }
    
    /** 获取用户名对应的用户信息 */
    private static Object chkUser(String userAccount) throws Exception
    {
        if (Validates.isMobile11(userAccount))
        {
            UserInfo user = Global.get(ZTable.class).item(UserInfo.class, new Selector("userMobile", userAccount));
            return user == null?"您的手机号未注册，请先注册":user;
        }
        else if (Validates.isEmail(userAccount))
        {
            UserInfo user = Global.get(ZTable.class).item(UserInfo.class, new Selector("userEmail", userAccount));
            return user == null?"您的邮箱未注册，请先注册":user;
        }
        else
        {//输入不正确
            return "请输入正确的邮箱或手机号";
        }
    }
    
    /** 检查账号是否已注册 */
    private static String chkRegister(String userAccount, int userAccountType) throws Exception
    {
        int count = Global.get(ZTable.class).count(UserAccount.class, new Selector("userAccount", userAccount));
        if (count == 0)
            return null;
        
        return "您的"+(userAccountType==ACCOUNT_TYPE_MOBILE?"手机号":"邮箱")+"已注册，请通过[忘记密码]找回";
    }
    
    /** 检查账号是否未注册 */
    private static String chkForgetPass(String userAccount, int userAccountType) throws Exception
    {
        int count = Global.get(ZTable.class).count(UserAccount.class, new Selector("userAccount", userAccount));
        if (count > 0)
            return null;
        
        return "您的"+(userAccountType==ACCOUNT_TYPE_MOBILE?"手机号":"邮箱")+"未注册，请先注册";
    }
    
    /** 检查验证码 */
    public static String chkValidCode(String userAccount, String verificationCode, int verificationType) throws Exception
    {
        Selector selector = new Selector("userAccount", userAccount)
            .addMust("verificationType", verificationType)
            .addMust("verificationCode", verificationCode)
            .addMust("verificationStatus", 0)
            .addMaybeThenGE("verificationEndTime", DateTimes.getDateTimeString())
            .addOrderbyDesc("verificationEndTime");
        
        return Global.get(ZTable.class).count(UserVerification.class, selector) == 0?"验证码不正确或已失效":null;
    }
}
